package utils

import (
	"fmt"
	"sync"
	"time"

	"github.com/jmoiron/sqlx"
	_ "github.com/lib/pq"
)

const (
	maxOpenConns = 10
	maxIdleConns = 5
)

// DB 是一个包含sqlx.DB的结构体，用于封装数据库操作
type DB struct {
	db  *sqlx.DB
	mu  sync.Mutex
	url string
}

var (
	globalDB *DB
	once     sync.Once
)

// InitializeDB 使用给定的URL初始化全局数据库连接，并确保此操作仅执行一次
func InitializeDB(url string) error {
	var err error
	once.Do(func() {
		globalDB, err = newDBInstance(url)
	})
	return err
}

// Close 关闭全局数据库连接
func CloseDB() error {
	return DbClose()
}

// newDBInstance 创建一个新的DB实例
func newDBInstance(url string) (*DB, error) {
	db, err := sqlx.Open("postgres", url)
	if err != nil {
		return nil, fmt.Errorf("failed to open database: %w", err)
	}

	// 设置连接池配置
	db.SetMaxOpenConns(maxOpenConns)
	db.SetMaxIdleConns(maxIdleConns)
	db.SetConnMaxLifetime(time.Minute * 5)

	return &DB{
		db:  db,
		url: url,
	}, nil
}

// DbClose 关闭数据库连接
func DbClose() error {
	if globalDB == nil {
		return fmt.Errorf("database not initialized")
	}
	globalDB.mu.Lock()
	defer globalDB.mu.Unlock()
	return globalDB.db.Close()
}

// DbExecuteSQL 执行SQL语句
func DbExecuteSQL(query string, args ...interface{}) (int64, error) {
	if globalDB == nil {
		return 0, fmt.Errorf("database not initialized")
	}
	globalDB.mu.Lock()
	defer globalDB.mu.Unlock()

	result, err := globalDB.db.Exec(query, args...)
	if err != nil {
		fmt.Println(query)
		fmt.Println("Error executing SQL:", err)
		return 0, err
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return 0, fmt.Errorf("error getting rows affected: %w", err)
	}

	return rowsAffected, nil
}

// DbSelect 查询数据并映射到结构体切片
func DbSelect(dest interface{}, query string, args ...interface{}) error {
	if globalDB == nil {
		return fmt.Errorf("database not initialized")
	}
	globalDB.mu.Lock()
	defer globalDB.mu.Unlock()
	return globalDB.db.Select(dest, query, args...)
}

// DbGet 查询单个记录并映射到结构体
func DbGet(dest interface{}, query string, args ...interface{}) error {
	if globalDB == nil {
		return fmt.Errorf("database not initialized")
	}
	globalDB.mu.Lock()
	defer globalDB.mu.Unlock()
	return globalDB.db.Get(dest, query, args...)
}
